package com.d2rv.camera;

import com.d2rv.offscreen.OffScreen3D;
import processing.core.PVector;

/**
 * This class creates a camera with a position, a target and a vector up which 
 * is used to create stereoscopic vision. 
 * 
 * @author Darrieutort A., Dupouy A., Rossignol L., Van Poucke A.
 */
public class Camera {

    private PVector position;
    private PVector target;
    private PVector up;
    private float near, far;
    private PVector screenSize;
    private float zoom;

    /**
     * Default constructor.
     * 
     * @param position the position of the camera
     * @param target the target of the camera
     * @param up the up of the camera
     * @param screenSize the real screen size
     */
    public Camera(PVector position, PVector target, PVector up, PVector screenSize) {
        this.position = position;
        this.target = target;
        this.up = up;
        this.zoom = 5;
        this.near = 0.05f;
        this.far = 10000f;
        this.screenSize = screenSize;
    }

    /**
     * Computes the frustum of the camera.
     * 
     * @param parent the current offscreen
     * @param offset value of the offset eye 
     */
    public void setCamera(OffScreen3D parent, float offset) {
        this.position.z += offset;

        parent.camera(this.position.x, this.position.y, this.position.z, 0, 0,
                this.position.z, this.up.x, this.up.y, this.up.z);

        float nearFactor = this.near / (this.position.x);

        float left = nearFactor * (-this.screenSize.x / 2f - this.position.z);
        float right = nearFactor * (this.screenSize.x / 2f - this.position.z);
        float top = nearFactor * (this.screenSize.y / 2f - this.position.y);
        float bottom = nearFactor * (-this.screenSize.y / 2f - this.position.y);

        parent.frustum(left, right, bottom, top, this.near, this.far);

        this.position.z -= offset;
    }

    /**
     * Updates the position of the camera.
     * 
     * @param position the new position of the camera
     */
    public void position(PVector position) {
        this.position = position;
        this.position.y *= this.zoom / 5;
    }

    /**
     * Returns the position of the camera.
     * 
     * @return the position of the camera
     */
    public PVector position() {
        return this.position;
    }

    /**
     * Updates the target of the camera.
     * 
     * @param target the new target of the camera
     */
    public void target(PVector target) {
        this.target = target;
    }

    /**
     * Returns the target of the camera.
     * 
     * @return the target of the camera
     */
    public PVector target() {
        return this.target;
    }

    /**
     * Returns the up of the camera.
     * 
     * @return the up of the camera
     */
    public PVector up() {
        return this.up;
    }

    /**
     * Updates the zoom factor of the camera used in the guide map.
     * 
     * @param zoom zoom factor
     */
    public void zoom(float zoom) {
        if (zoom >= 5) {
            this.zoom = zoom;
        }
    }

    /**
     * Returns the zoom factor of the camera used in the guide map.
     * 
     * @return the zoom factor
     */
    public float zoom() {
        return this.zoom;
    }
}